using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Content;

namespace MoEngine
{
    public enum MouseButtons : int
    {
        Left,
        Middle,
        Right,
        Undefined
    }

    public class InputEventArgs : EventArgs
    {
        public MouseButtons Button;
        public MouseState Mouse;
        public KeyboardState Keys;
        public GameTime Time;
    }

    public enum MouseEventType
    {
        MouseMove,
        MouseDown,
        MouseUp,        
    }

    public delegate void MouseButtonEvent(MouseButtons button, Point mousePos, InputEventArgs evtArgs);
    public delegate void MouseMoveEvent(Point mousePos, InputEventArgs evtArgs);

    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class InputManager : CoreComponent
    {
        MouseState m_LastMouse;

        public event MouseButtonEvent MouseDown;
        public event MouseButtonEvent MouseUp;
        public event MouseMoveEvent MouseMove;

        public InputManager()
        {
            // TODO: Construct any child components here
        }

        /// <summary>
        /// Allows the game component to perform any initialization it needs to before starting
        /// to run.  This is where it can query for any required services and load content.
        /// </summary>
        public override void Initialize()
        {
            // TODO: Add your initialization code here

            base.Initialize();
        }

        public InputEventArgs NewEventArgs(MouseButtons button, GameTime gameTime)
        {
            // TODO: allocate couple memory each frame is probably not a good idea
            InputEventArgs evtArgs = new InputEventArgs();
            evtArgs.Button = button;
            evtArgs.Mouse = Mouse.GetState();
            evtArgs.Keys = Keyboard.GetState();
            evtArgs.Time = gameTime;

            return evtArgs;
        }

        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public override void Update(GameTime gameTime)
        {
            // TODO: Add your update code here
            
            // prepare a input event args
            //InputEventArgs evtArgs = NewEventArgs(gameTime);

            // Check mouse state change and post events
            MouseState mouse = Mouse.GetState();
            // Check moving
            if (mouse.X != m_LastMouse.X || mouse.Y != m_LastMouse.Y)
            {
                MouseMoveEvent tempMouseMove = MouseMove;
                if (tempMouseMove != null)
                {
                    tempMouseMove(new Point(mouse.X, mouse.Y), NewEventArgs(MouseButtons.Undefined, gameTime));
                }
            }

            // Check each button
            ButtonState[] lastBtnStates = 
                { m_LastMouse.LeftButton, m_LastMouse.MiddleButton, m_LastMouse.RightButton };

            ButtonState[] curBtnStates = 
                { mouse.LeftButton, mouse.MiddleButton, mouse.RightButton };

            // Check keyboard state change and post events
            for (int i = 0; i < lastBtnStates.Length; i++)
            {
                if (curBtnStates[i] != lastBtnStates[i])
                {
                    if (curBtnStates[i] == ButtonState.Pressed)
                    {
                        if (MouseDown != null)
                            MouseDown((MouseButtons)i, new Point(mouse.X, mouse.Y), NewEventArgs((MouseButtons)i, gameTime));
                    }
                    else
                    {
                        if (MouseUp != null)
                            MouseUp((MouseButtons)i, new Point(mouse.X, mouse.Y), NewEventArgs((MouseButtons)i, gameTime));
                    }
                }
            }

            // cache the last mouse
            m_LastMouse = mouse;

            base.Update(gameTime);
        }
    }
}